#include "Col.ph"
#ifdef __GNUG__
#pragma implementationy
#endif

#include "System.h"

#include "Class.h"
#include "File.h"
#include "String.h"
#include "Error.h"
#include "OrdColl.h"
#include "Env.h"
#include "SysEvtHandler.h"
#include "Timer.h"
#include "ClassManager.h"
#include "ObjectTable.h"
#include "ET_stdlib.h"
#include "ET_errno.h"

const char
	*gEtDir,
	*gProgname= "*** progname not set ***",
	*gProgPath;
bool
	gInMain,
	gQuitApp;
int     gDebug;
System  *gSystem;

static int _initTermCnt;

void InitSystem()
{
	if (gSystem == 0) {
		if (! gInMain)
			::Fatal("InitSystem", "InitSystem called before ETInit");

		gSystem= (System*) gClassManager->Load("unix", "UnixSystem");
		if (gSystem == 0)
			gSystem= new System;
		
		if (gEtDir == 0) {
			gEtDir= gSystem->Getenv("ET_DIR");
			if (gEtDir == 0)
				gEtDir= "/local/et";
		}
		if (gSystem->Init())
			::Fatal("InitSystem", "can't init operating system layer");
	}
}

void ETInit(int argc, char **argv)
{
	char *a0;

	if (gSystem)
		return;

	if (argc > 0 && argv && argv[0])
		gProgname= argv[0];

	gInMain= TRUE;
	InitSystem();

	for (int i= 1; i < argc; i++) {
		if ((a0= argv[i]) && ((int)a0 != -1)) {
			if (a0[0] == '-' && a0[1] == 'E' && a0[2]) {
				a0= &a0[2];
				if (strcmp(a0, "d") == 0)
					a0= "Debug";
				Env::SetValue(a0, eEnvCommand);
				gDebug= Env::GetValue("System.Debug", 0);
			}
		}
	}
		
	gDebug= Env::GetValue("System.Debug", 0);

	if (Env::GetValue("System.MemStat", 0))
		Storage::EnableStatistics();
	int msize= Env::GetValue("System.MemStat.size", -1);
	int mcnt= Env::GetValue("System.MemStat.cnt", -1);
	if (msize != -1 || mcnt != -1)
		Storage::EnableStatistics(msize, mcnt);
}

void _InitModule()
{
	_initTermCnt++;
}

void _TermModule()
{
	_initTermCnt--;
	if (_initTermCnt == 0) {
		ObjectTable::Terminate();
		File::Cleanup();
		Storage::FreeAll();
	}
}

static void ETCleanup()
{
	SafeDelete(gSystem);
}

#undef main

int main(int argc, char *argv[])
{
	ETInit(argc, argv);
	int rc= ETMain(argc, argv);
	if (gDebug)
		fprintf(stderr, "main(%d, ...) = %d\n", argc, rc);
	ETCleanup();
	return rc;
}

//---- System ------------------------------------------------------------------

NewMetaImpl(System, Object, (TS(errorstr), TS(name), TB(done), TP(onExitList),
	T(level), TP(fileHandler), TP(signalHandler), TP(cleanupList),
	TS(gEtDir), TS(gProgname), TB(gInMain), TB(gDebug), TB(gQuitApp)));

System::System(const char *nm)
{
	name= nm;
	level= 0;
}

void System::remove(SeqCollection *&oc)
{
	if (oc) {
		Iter next(oc);
		register SysEvtHandler *seh;
		while (seh= (SysEvtHandler*) next())
			gSystem->AddCleanupObject(seh);
		SafeDelete(oc);
	}
}

System::~System()
{
	if (onExitList) {
		onExitList->FreeAll();
		delete onExitList;
		onExitList= 0;
	}

	remove(fileHandler);
	remove(signalHandler);

	if (gSystem == this)
		gSystem= 0;
		
	//Remove();
}

//---- Misc --------------------------------------------------------------------

bool System::Init()
{
	level= 0;

	fileHandler= new OrdCollection;
	signalHandler= new OrdCollection;
	cleanupList= 0;

	return FALSE;
}

const char *System::GetError()
{
	return form("errno: %d", errno);
}

void System::RemoveOnExit(Object *op)
{
	if (onExitList == 0)
		onExitList= new OrdCollection;
	if (onExitList->FindPtr(op) == 0)
		onExitList->Add(op);
}

const char *System::AsString()
{
	return name;
}

//---- EventLoop ---------------------------------------------------------------

void System::Control()
{
	done= FALSE;
	while (! done) {
		InnerLoop();
		if (level == 0 && cleanupList)
			Remove();
	}
}

void System::ExitControl()
{
	gQuitApp= done= TRUE;
}

void System::InnerLoop()
{
	level++;
	DispatchOneEvent();
	level--;
}

void System::Wait(u_int duration)
{
	Timer th(duration);

	level++;
	while (!th.WasTimeout())
		DispatchOneEvent();
	level--;
}

void System::DispatchOneEvent()
{
	AbstractMethod("DispatchOneEvent");
}

//---- handling of system events -----------------------------------------------

Time System::Now()
{
	return Time(0);
}

long System::NextTimeout(Timer *head)
{
	long timeout;
	
	if (head) {
		timeout= head->GetAbsTime() - gSystem->Now();
		if (timeout < 0)
			timeout= 0;
	} else
		timeout= -1;    // no timeout
	return timeout;
}

void System::AddTimer(Timer *ti)
{
	register Timer *t, *p, **th= ti->IsSync() ? &syncTimers : &asyncTimers;
	
	for (t= *th, p= 0; t; p= t, t= t->next)
		if (ti->abstime < t->abstime)
			break;
	if (p)
		th= &p->next;
	ti->next= *th;
	*th= ti;
}

bool System::RemoveTimer(Timer *ti)
{
	register Timer *t, *p, **th= ti->IsSync() ? &syncTimers : &asyncTimers;

	for (t= *th, p= 0; t; p= t, t= t->next) {
		if (t == ti) {
			if (p)
				p->next= t->next;
			else
				*th= t->next;
			ti->next= 0;
			return TRUE;
		}
	}
	return FALSE;
}

void System::AddSignalHandler(SignalHandler *h)
{
	if (h && signalHandler && (signalHandler->FindPtr(h) == 0))
		signalHandler->Add(h);
}

SignalHandler *System::RemoveSignalHandler(SignalHandler *h)
{
	if (signalHandler)
		return (SignalHandler*) signalHandler->RemovePtr(h);
	return 0;
}

void System::AddFileHandler(FileHandler *h)
{
	if (h && fileHandler && (fileHandler->FindPtr(h) == 0))
		fileHandler->Add(h);
}

FileHandler *System::RemoveFileHandler(FileHandler *h)
{
	if (fileHandler)
		return (FileHandler*) fileHandler->RemovePtr(h);
	return 0;
}

void System::AddCleanupObject(Object *op)
{
	if (op) {
		if (fileHandler)
			fileHandler->RemovePtr(op);
		if (signalHandler)
			signalHandler->RemovePtr(op);
		if (cleanupList == 0)
			cleanupList= new OrdCollection;
		if (cleanupList->FindPtr(op) == 0)
			cleanupList->Add(op);
	}
}

void System::Remove()
{
	while (cleanupList != 0) {
		SeqCollection *tmp= cleanupList;
		cleanupList= 0;
		tmp->FreeAll();
		delete tmp;
	}
}

//---- Processes ---------------------------------------------------------------

int System::Exec(const char*)
{
	AbstractMethod("Exec");
	return -1;
}

FILE *System::OpenPipe(const char*, const char*)
{
	AbstractMethod("OpenPipe");
	return 0;
}

int System::ClosePipe(FILE*)
{
	AbstractMethod("ClosePipe");
	return -1;
}

int System::GetPid()
{
	AbstractMethod("GetPid");
	return -1;
}

void System::Exit(int, bool)
{
	AbstractMethod("Exit");
}

void System::Abort(int)
{
	AbstractMethod("Abort");
}

//---- Directories -------------------------------------------------------------

void *System::MakeDirectory(const char*)
{
	AbstractMethod("MakeDirectory");
	return 0;
}

void System::FreeDirectory(void*)
{
	AbstractMethod("FreeDirectory");
}

const char *System::GetDirEntry(void*)
{
	AbstractMethod("GetDirEntry");
	return 0;
}

bool System::ChangeDirectory(const char*)
{
	AbstractMethod("ChangeDirectory");
	return FALSE;
}

const char *System::WorkingDirectory()
{
	return 0;
}

const char *System::HomeDirectory()
{
	return 0;
}

const char *System::DirName(const char *pathname)
{
	if (strchr(pathname, '/')) {
		static char buf[1000];
		strcpy(buf, pathname);
		char *r= strrchr(buf, '/');
		if (r != buf)
			*r= '\0';
		return buf;
	}
	return WorkingDirectory();
}

//---- Paths & Files -----------------------------------------------------------

bool System::ExpandPathName(char*, int)
{
	return FALSE;
}

bool System::AccessPathName(const char*, int)
{
	return FALSE;
}

void System::Rename(const char *from, const char *to)
{
	File::Unlink(to);
	if (File::Link(from, to) != -1)
		File::Unlink(from);
}

int System::GetPathInfo(const char*, u_long*, u_long*, u_long*, u_long*)
{
	AbstractMethod("GetPathInfo");
	return -1;
}

//---- environment manipulation ------------------------------------------------

void System::Setenv(const char*, const char*)
{
	AbstractMethod("Setenv");
}

void System::Unsetenv(const char *name)
{
	Setenv(name, 0);
}

const char *System::Getenv(const char*)
{
	AbstractMethod("Getenv");
	return 0;
}

const char *System::GetEnvAt(int)
{
	AbstractMethod("GetEnvAt");
	return 0;
}

//---- Dynamic Loading ---------------------------------------------------------

Object *System::Load(const char*, const char*)
{
	AbstractMethod("Load");
	return 0;
}

//---- RPC ---------------------------------------------------------------------

int System::OpenConnection(const char*, const char*)
{
	AbstractMethod("OpenConnection");
	return -1;
}

int System::AnnounceTcpService(const char*)
{
	AbstractMethod("AnnounceTcpService");
	return -1;
}

int System::AnnounceUnixService(const char*)
{
	AbstractMethod("AnnounceUnixService");
	return -1;
}

int System::AcceptConnection(int)
{
	AbstractMethod("AcceptConnection");
	return -1;
}

//---- Pseudo TTYs -------------------------------------------------------------

PttyImpl *System::MakePttyImpl()
{
	AbstractMethod("MakePttyImpl");
	return 0;
}

